﻿using MiniFox.Caching;
using MiniFox.Components;
using MiniFox.Infrastructure;
using MiniFox.Platform.Exceptions;
using MiniFox.Platform.Models;
using MiniFox.Platform.OAuth2.Configurations;
using MiniFox.Platform.OAuth2.Events;
using MiniFox.Platform.OAuth2.Providers;
using MiniFox.Platform.Providers;
using MiniFox.Security.Text;
using MiniFox.Utility;
using MiniFox.Web;

namespace MiniFox.Platform.OAuth2.Services
{
    /// <summary>
    /// 授权服务
    /// </summary>
    [AutoResolve]
    public class AuthorizationService : OAuth2ServiceBase, IAuthorizationService
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="authStateManagement">认证状态管理器</param>
        public AuthorizationService(IAuthenticationStateManagement authStateManagement) : base(authStateManagement)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        public AuthorizationConfiguration Configuration
        {
            get
            {
                return this.ConfigProvider.AuthorizationConfiguration;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [AutoWired]
        protected IAuthorizationProvider AuthProvider { get; set; }
        /// <summary>
        /// 
        /// </summary>
        [AutoWired]
        protected ICipherProvider CipherProvider { get; set; }
        
        /// <summary>
        /// 检查客户端地址
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="redirectUri"></param>
        /// <returns></returns>
        public void CheckClient(string appId, string redirectUri)
        {
            //如果不在例外域名列表中，则检查是否与appId匹配
            if (this.Configuration.ExceptionDomains.Any(domain => redirectUri.StartsWith(domain)))
                return;

            if (!this.AuthProvider.CheckClient(appId, redirectUri))
                throw new PlatformException("MSG_UnknownClientApp");

        }

        #region 事件

        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<SignOutEventArgs> SignOutEvent;
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected void OnSignOut(SignOutEventArgs e)
        {
            this.SignOutEvent?.Invoke(this, e);
        }
        #endregion

        #region 授权码/令牌
        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="context"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        protected string CreateAuthorizeCode(string appId, string context, AuthScope scope)
        {
            if (this.PassportToken == null)
                return null;

            //通行证正常情况下直接放码
            var authCode = Tools.GenerateRandomHashCode();
            if (!this.AuthProvider.SaveAuthorizeCode(this.PassportToken, authCode, appId, scope.ToString(), context, Configuration.CodeExpires))
            {
                throw new MiniFox.Exceptions.SystemException();
            }
            return authCode;
        }
        /// <summary>
        /// 创建授权数据(授权码)
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="redirectUri"></param>
        /// <param name="context"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        public RedirectResponse CreateAuthorizeData(string appId, string redirectUri, string context, AuthScope scope)
        {
            if (string.IsNullOrEmpty(redirectUri))
            {
                redirectUri = this.Configuration.DefaultUri;
            }
            else
            {
                this.CheckClient(appId, redirectUri);
            }
            var authCode = this.CreateAuthorizeCode(appId, context, scope);
            Dictionary<string, string> authData = new();
            string targetUri;
            if (string.IsNullOrEmpty(authCode)) //放码不成功的情况下，临时保存各个参数并跳转登录页
            {
                //将原始请求数据存入字典
                authData.Add(OpenAuthKey.AUTH_APP_ID, appId);
                authData.Add(OpenAuthKey.AUTH_CLIENT_CONTEXT, context);
                authData.Add(OpenAuthKey.AUTH_REDIRECT_URI, redirectUri);
                authData.Add(OpenAuthKey.AUTH_RESPONSE_TYPE, AuthResponseType.AuthCode.ToString());
                authData.Add(OpenAuthKey.AUTH_GRANT_TYPE, scope.ToString());
                targetUri = CreateSigninUri(authData);
            }
            else
            {
                authData.Add(OpenAuthKey.AUTH_CODE, authCode);
                authData.Add(OpenAuthKey.AUTH_CLIENT_CONTEXT, context);
                redirectUri = redirectUri.RemoveQueryString(OpenAuthKey.AUTH_CODE, OpenAuthKey.AUTH_CLIENT_CONTEXT);
                targetUri = redirectUri.AddQueryString<string, string>(authData);
            }
            return new RedirectResponse<IDictionary<string, string>>(authData)
            {
                RedirectUri = targetUri
            };
        }
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="redirectUri"></param>
        ///// <returns></returns>
        //public RedirectResponse CreateAuthorizeData(string redirectUri)
        //{
        //    if (string.IsNullOrEmpty(redirectUri))
        //    {
        //        redirectUri = this.Configuration.RootUri;
        //    }

        //    Dictionary<string, string> authData = new Dictionary<string, string>();
        //    string targetUri;
        //    if (this.PassportToken == null)
        //    {
        //        //将原始请求数据存入字典
        //        authData.Add(OpenAuthKey.AUTH_REDIRECT_URI, redirectUri);
        //        authData.Add(OpenAuthKey.AUTH_RESPONSE_TYPE, AuthResponseType.PwdCredentials.ToString());
        //        targetUri = CreateSigninUri(authData);
        //    }
        //    else
        //    {
        //        targetUri = redirectUri;
        //    }
        //    return new RedirectResponse
        //    {
        //        RedirectUri = targetUri
        //    };
        //}
        /// <summary>
        /// 创建登录链接
        /// </summary>
        /// <param name="authData"></param>
        /// <returns></returns>
        protected virtual string CreateSigninUri(Dictionary<string, string> authData)
        {
            //出票
            var authTicket = new Ticket(SequenceGUID.NewGUID(), Configuration.TicketExpires);
            //保存授权票据后转至登录页
            if (authData != null && authData.Count > 0 && this.TicketProvider.SetTicketData(authTicket, TicketType.Authorization, authData))
            {
                //缓存票根
                Cache.Add(authTicket.TicketID, authData, authTicket.Duration);
            }
            return this.Configuration.SignInUri.AddQueryString(OpenAuthKey.AUTH_TICKET, authTicket);
        }
        #endregion
        
        #region 注销
        /// <summary>
        /// 注销token
        /// </summary>
        /// <param name="redirectUri"></param>
        /// <returns></returns>
        public RedirectResponse SignOut(string redirectUri)
        {
            var passportToken = this.PassportToken;
            if (passportToken != null)
            {
                this.TokenProvider.DisablePassportToken(passportToken);
                Cache.Remove($"{OpenAuthKey.PASSPORT_TOKEN}-{passportToken.TicketID}");
            }
            this.OnSignOut(new SignOutEventArgs(passportToken));
            this.StateManagement.Clear();

            string targetUri;
            if (string.IsNullOrEmpty(redirectUri)) // 无回跳地址时，自动定向到登录页
            {
                targetUri = this.Configuration.DefaultUri;
            }
            else
            {
                targetUri = redirectUri;
            }

            return new RedirectResponse
            {
                RedirectUri = targetUri
            };
        }

        #endregion

        #region 修改密码
        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="oldPwd"></param>
        /// <param name="newPwd"></param>
        /// <returns></returns>
        public bool ChangePassword(string oldPwd, string newPwd)
        {
            Cipher oldPassword = this.CipherProvider.CreateCipher(oldPwd);
            Cipher newPassword = this.CipherProvider.CreateCipher(newPwd);
            var token = this.StateManagement.AuthTicketID;
            return this.AuthProvider.ResetPassword(token, oldPassword, newPassword);
        }
        #endregion
    }
}
